package org.cos.generation.ast;


import java.util.ArrayList;
import java.util.List;

import org.cos.generation.data.DataFlowAnalyzer;
import org.cos.generation.epda.EPDAGenerator;
import org.cos.generation.util.info.ClassInfo;
import org.cos.generation.util.info.ClassTable;
import org.cos.generation.util.symbol.SymbolTable;
import org.eclipse.jdt.core.dom.*;


public class CoSASTVisitor extends ASTVisitor {
	
	private ClassTable classTable;
	
	private DataFlowAnalyzer dataFlowAnalyzer;
	
	private EPDAGenerator epdaGenerator;
		
	public List<ClassInfo> process(CompilationUnit unit) {
		// Create Class Table.
		classTable = new ClassTable();
		dataFlowAnalyzer = new DataFlowAnalyzer(
				new SymbolTable(), classTable);
		epdaGenerator = new EPDAGenerator(dataFlowAnalyzer);
		// Scan.
		unit.accept(this);
		// Get ClassInfo list.
		List<ClassInfo> classArray = 
				new ArrayList<ClassInfo>(classTable.values());
		return classArray;
	}
	
	@Override
	public boolean visit(CompilationUnit node) {
		epdaGenerator.enterUnit(node, classTable);
		return true;
	}
	
	@Override
	public boolean visit(TypeDeclaration node) {
		dataFlowAnalyzer.enterClass(node);
		epdaGenerator.enterClass(node, classTable);
		return true;
	}
	
	@Override
	public void endVisit(TypeDeclaration node) {
		dataFlowAnalyzer.exitClass(node);
	}
	
	@Override
	public boolean visit(MethodDeclaration node) {
		dataFlowAnalyzer.enterMethod(node);
		epdaGenerator.enterMethod(node);
		return true;
	}
	
	@Override
	public void endVisit(MethodDeclaration node) {
		dataFlowAnalyzer.exitMethod(node);
		epdaGenerator.exitMethod(node);
	}
	
	@Override
	public boolean visit(Block node) {
		dataFlowAnalyzer.enterBlock(node);
		return true;
	}
	
	@Override
	public void endVisit(Block node) {
		dataFlowAnalyzer.exitBlock(node);
	}
	
	@Override
	public boolean visit(IfStatement node) {
		dataFlowAnalyzer.enterIf(node);
		epdaGenerator.enterIf(node);
		// Visit condition node.
		ASTNode conditionNode = node.getExpression();
		if (conditionNode != null) {
			conditionNode.accept(this);
		}
		// Visit then node.
		ASTNode thenNode = node.getThenStatement();
		if (thenNode != null) {
			thenNode.accept(this);
		}
		// Visit else node.
		ASTNode elseNode = node.getElseStatement();
		if (elseNode != null) {
			dataFlowAnalyzer.beforeElse(node);
			epdaGenerator.beforeElse(node);
			elseNode.accept(this);
		}
		dataFlowAnalyzer.exitIf(node);
		epdaGenerator.exitIf(node);
		return false;
	}
	
	@Override
	public boolean visit(ForStatement node) {
		dataFlowAnalyzer.enterFor(node);
		epdaGenerator.enterFor(node);
		return true;
	}
	
	@Override
	public void endVisit(ForStatement node) {
		dataFlowAnalyzer.exitFor(node);
		epdaGenerator.exitFor(node);
	}
	
	@Override
	public boolean visit(WhileStatement node) {
		epdaGenerator.enterWhile(node);
		return true;
	}
	
	@Override
	public void endVisit(WhileStatement node) {
		epdaGenerator.exitWhile(node);
	}
	
	@Override
	public boolean visit(DoStatement node) {
		epdaGenerator.enterDo(node);
		return true;
	}
	
	@Override
	public void endVisit(DoStatement node) {
		epdaGenerator.exitDo(node);
	}

	@Override
	public boolean visit(VariableDeclarationExpression node)  {
		dataFlowAnalyzer.enterDeclaration(node);
		return true;
	}

	@Override
	public boolean visit(VariableDeclarationStatement node)  {
		dataFlowAnalyzer.enterDeclaration(node);
		return true;
	}
	
	@Override
	public boolean visit(FieldDeclaration node) {
		dataFlowAnalyzer.enterDeclaration(node);
		return true;
	}
	
	@Override
	public boolean visit(Assignment node) {
		dataFlowAnalyzer.enterAssignment(node);
		return true;
	}
	
	@Override
	public boolean visit(MethodInvocation node) {
		dataFlowAnalyzer.enterInvocation(node);
		epdaGenerator.enterInvocation(node);
		return true;
	}
	
	public boolean visit(ReturnStatement node) {
		epdaGenerator.enterReturn(node);
		return true;
	}
	
	public boolean visit(ClassInstanceCreation node) {
		epdaGenerator.enterInstance(node);
		return true;
	}
	
}
